#汇编声明了两个宏，分别用于保存和加载通用寄存器。
.altmacro
.macro SAVE_GP n
    sd x\n, \n*8(sp)
.endm
.macro LOAD_GP n
    ld x\n, \n*8(sp)
.endm
    .section .text
    .globl __alltraps
    .globl __restore
    #地址4字节对齐
    .align 2 
#保存TrapContext上下文的汇编代码：             
__alltraps:
    # csrrw 是CSR相关原子指令，交换寄存器 sscratch 和 sp 的值，此时sscratch的值应为内核栈顶
    csrrw sp, sscratch, sp
    # now sp->kernel stack, sscratch->user stack
    # allocate a TrapContext on kernel stack
    addi sp, sp, -34*8
    # save general-purpose registers
    sd x1, 1*8(sp)
    # skip sp(x2), we will save it later
    sd x3, 3*8(sp)
    # skip tp(x4), application does not use it
    # save x5~x31，循环调宏SAVE_GP 保存寄存器x5~x31的值到内核栈
    .set n, 5
    .rept 27
        SAVE_GP %n
        .set n, n+1
    .endr
    # we can use t0/t1/t2（寄存器x5,x6,x7）freely, because they were saved on kernel stack，CSR类寄存器只能由专门CSR指令访问，保存sstatus、sepc
    csrr t0, sstatus
    csrr t1, sepc
    sd t0, 32*8(sp)
    sd t1, 33*8(sp)
    # read user stack from sscratch and save it on the kernel stack
    csrr t2, sscratch
    sd t2, 2*8(sp)
    # set input argument of trap_handler(cx: &mut TrapContext)，此时sp为内核栈顶，相当于将TrapContext的地址传给trap_handler函数
    mv a0, sp
    call trap_handler

#恢复TrapContext上下文的汇编代码：
__restore:
    # case1: start running app by __restore，此情况时必须有mv sp, a0，此时a0为内核栈顶
    # case2: back to U after handling trap
    mv sp, a0
    # now sp->kernel stack(after allocated), sscratch->user stack
    # restore sstatus/sepc
    ld t0, 32*8(sp)
    ld t1, 33*8(sp)
    ld t2, 2*8(sp)
    csrw sstatus, t0
    csrw sepc, t1
    ##此处恢复的sscratch值为用户栈顶
    csrw sscratch, t2 
    # restore general-purpuse registers except sp/tp
    ld x1, 1*8(sp)
    ld x3, 3*8(sp)
    .set n, 5
    .rept 27
        LOAD_GP %n
        .set n, n+1
    .endr
    # release TrapContext on kernel stack
    addi sp, sp, 34*8
    # now sp->kernel stack, sscratch->user stack
    # 原子交换寄存器sp, sscratch的值，交换后sp指向用户栈顶
    csrrw sp, sscratch, sp
    # return from trap，特权指令sret功能：（1）CPU 会将当前的特权级按照sstatus的SPP字段设置为U或者S；（2）CPU会跳转到sepc寄存器指向的那条指令继续执行
    sret    